#include "Task.hpp"  // 包含任务相关的头文件，定义了任务类型和函数
#include <string>    // 字符串操作
#include <vector>    // 使用vector容器
#include <cstdlib>   // 系统函数
#include <ctime>     // 时间函数
#include <cassert>   // 断言
#include <unistd.h>  // Unix标准函数
#include <sys/stat.h>    // 文件状态
#include <sys/wait.h>    // 进程等待

const int processnum = 10;      // 定义进程池中的进程数量
std::vector<task_t> tasks;      // 存储所有可执行的任务

// channel类：管理父子进程间的通信通道
class channel
{
public:
    // 构造函数：初始化通信管道
    channel(int cmdfd, int slaverid, const std::string &processname)
    :_cmdfd(cmdfd), _slaverid(slaverid), _processname(processname)
    {}
public:
    int _cmdfd;               // 命令管道的文件描述符（写端）
    pid_t _slaverid;          // 对应子进程的进程ID
    std::string _processname; // 进程的名称，用于显示和日志
};

// 子进程的主要执行函数
void slaver()
{
    while(true)
    {
        int cmdcode = 0;
        // 从标准输入读取命令（标准输入被重定向到了管道）
        int n = read(0, &cmdcode, sizeof(int)); 
        if(n == sizeof(int))
        {
            // 收到命令后打印信息并执行对应任务
            std::cout <<"slaver say@ get a command: "<< getpid() << " : cmdcode: " <<  cmdcode << std::endl;
            if(cmdcode >= 0 && cmdcode < tasks.size()) tasks[cmdcode]();
        }
        if(n == 0) break;  // 管道被关闭，退出循环
    }
}

// 初始化进程池，创建子进程和通信管道
void InitProcessPool(std::vector<channel> *channels)
{
    std::vector<int> oldfds;  // 存储已创建的管道文件描述符
    for(int i = 0; i < processnum; i++)
    {
        int pipefd[2];
        int n = pipe(pipefd);  // 创建管道，pipefd[0]读端，pipefd[1]写端
        assert(!n);
        (void)n;

        pid_t id = fork();     // 创建子进程
        if(id == 0) // 子进程执行的代码
        {
            // 关闭继承自父进程的所有历史文件描述符
            std::cout << "child: " << getpid() << " close history fd: ";
            for(auto fd : oldfds) {
                std::cout << fd << " ";
                close(fd);
            }
            std::cout << "\n";

            close(pipefd[1]);  // 关闭写端
            dup2(pipefd[0], 0);  // 将管道读端重定向到标准输入
            close(pipefd[0]);    // 关闭原读端
            slaver();            // 执行子进程的主要逻辑
            std::cout << "process : " << getpid() << " quit" << std::endl;
            exit(0);
        }
        // 父进程执行的代码
        close(pipefd[0]);  // 关闭读端

        // 创建新的channel对象并保存
        std::string name = "process-" + std::to_string(i);
        channels->push_back(channel(pipefd[1], id, name));
        oldfds.push_back(pipefd[1]);  // 保存文件描述符

        sleep(1);  // 等待1秒，确保进程创建的有序性
    }
}

// 打印所有channel的信息，用于调试
void Debug(const std::vector<channel> &channels)
{
    for(const auto &c :channels)
    {
        std::cout << c._cmdfd << " " << c._slaverid << " " << c._processname << std::endl;
    }
}

// 显示操作菜单
void Menu()
{
    std::cout << "################################################" << std::endl;
    std::cout << "# 1. 刷新日志             2. 刷新出来野怪        #" << std::endl;
    std::cout << "# 3. 检测软件是否更新      4. 更新用的血量和蓝量  #" << std::endl;
    std::cout << "#                         0. 退出               #" << std::endl;
    std::cout << "#################################################" << std::endl;
}

// 控制子进程执行任务的主函数
void ctrlSlaver(const std::vector<channel> &channels)
{
    int which = 0;  // 当前选择的进程索引
    while(true)
    {
        int select = 0;
        Menu();
        std::cout << "Please Enter@ ";
        std::cin >> select;

        if(select <= 0 || select >= 5) break;  // 退出条件
        
        int cmdcode = select - 1;  // 将选项转换为命令代码

        // 向选中的子进程发送任务，并打印信息
        std::cout << "father say: " << " cmdcode: " <<
            cmdcode << " already sendto " << channels[which]._slaverid << " process name: " 
                << channels[which]._processname << std::endl;
        
        write(channels[which]._cmdfd, &cmdcode, sizeof(cmdcode));

        which++;  // 轮询选择下一个进程
        which %= channels.size();
    }
}

// 清理进程池，关闭所有管道和进程
void QuitProcess(const std::vector<channel> &channels)
{
    for(const auto &c : channels){
        close(c._cmdfd);  // 关闭管道
        waitpid(c._slaverid, nullptr, 0);  // 等待子进程结束
    }
}

int main()
{
    LoadTask(&tasks);  // 加载任务列表
            
    // 初始化随机数种子
    srand(time(nullptr)^getpid()^1023);
    
    std::vector<channel> channels;
    InitProcessPool(&channels);  // 初始化进程池
    
    ctrlSlaver(channels);  // 运行任务分发循环
    
    QuitProcess(channels);  // 清理资源
    return 0;
}